import { autorun, observable } from 'mobx'
import { LS_ACCESS_TOKEN_KEY, LS_USER_KEY, NOT_INITIALIZED_ERROR } from './constants'
import { getTargetContainer, http, Query } from './utils'
import defaultTheme from './theme/default'
import moment from 'moment'
const response_type = 'code'
const grant_type = 'authorization_code'
const LS_ACCESS_TOKEN_KEY_DAY = LS_ACCESS_TOKEN_KEY + moment().format('YYYYMMDD');

function extendRenderer(instance, renderer) {
    instance[renderer] = (container) => {
        const targetContainer = getTargetContainer(container)
        const render = instance.theme[renderer] || instance.defaultTheme[renderer]

        autorun(() => {
            const e = render(instance.state, instance)
            if (targetContainer.firstChild) {
                targetContainer.replaceChild(e, targetContainer.firstChild)
            } else {
                targetContainer.appendChild(e)
            }
        })

        return targetContainer
    }
}

class Giteement{

    get accessToken() {
        let token = localStorage.getItem(LS_ACCESS_TOKEN_KEY_DAY);
        if (!token) {
            localStorage.clear();
        }
        return token
    }
    set accessToken(token) {
        localStorage.setItem(LS_ACCESS_TOKEN_KEY_DAY, token)
    }

    get loginLink() {
        const oauthUri = 'https://gitee.com/oauth/authorize'
        const redirect_uri = this.backcall_uri + '?bkurl='  + encodeURIComponent(window.location.href);
        const oauthParams = Object.assign({
            response_type,
            redirect_uri,
        }, this.oauth);
        return `${oauthUri}${Query.stringify(oauthParams)}`
    }
    constructor(options = {}) {
        this.defaultTheme = defaultTheme;
        this.useTheme(defaultTheme);

        Object.assign(this, {
            id: window.location.href,
            title: window.document.title,
            link: window.location.href,
            desc: '',
            labels: '',
            theme: defaultTheme,
            oauth: {},
            perPage: 20,
            maxCommentHeight: 250,
        }, options);
        this.useTheme(this.theme);

        const user = {};
        try {
            const userInfo = localStorage.getItem(LS_USER_KEY);
            if (this.accessToken && userInfo) {
                Object.assign(user, JSON.parse(userInfo), {
                    fromCache: true,
                })
            }
        } catch (e) {
            localStorage.removeItem(LS_USER_KEY)
            localStorage.removeItem(LS_ACCESS_TOKEN_KEY)
        }

        this.state = observable({
            user,
            error: null,
            meta: {},
            comments: undefined,
            currentPage: 1,
        })

        const query = Query.parse();
        if (query.code) {
            const { client_id, client_secret } = this.oauth
            const redirect_uri = this.backcall_uri + '?bkurl='
                + encodeURIComponent(window.location.origin + window.location.pathname + window.location.hash.split("?")[0]);
            const code = query.code
            delete query.code
            const search = Query.stringify(query)
            const replacedUrl = `${window.location.origin}${window.location.pathname}${search}` + window.location.hash.split("?")[0];
            history.replaceState({}, '', replacedUrl)
            Object.assign(this, {
                id: replacedUrl,
                link: replacedUrl,
            }, options)
            this.state.user.isLoggingIn = true
            http.post(this.oauth_uri, {
                code,
                client_id,
                client_secret,
                redirect_uri,
                grant_type
            }, '')
                .then(data => {
                    this.accessToken = data.access_token
                    this.update()
                })
                .catch(e => {
                    this.state.user.isLoggingIn = false
                    alert(e)
                })

        } else {
            this.update();
        }
    }

    init() {
        return this.createIssue()
            .then(() => this.loadComments())
            .then(comments => {
                this.state.error = null
                return comments
            })
    }

    useTheme(theme = {}) {
        this.theme = theme
        const renderers = Object.keys(this.theme)
        renderers.forEach(renderer => extendRenderer(this, renderer))
    }

    update() {
        return Promise.all([this.loadMeta(), this.loadUserInfo()])
            .then(() => Promise.all([
                this.loadComments(),
            ]))
            .catch(e => this.state.error = e)
    }

    markdown(text) {
        return http.post('/markdown', {
            text
        })
    }

    users(login) {
        return http.get('/users/' +login)
    }
    createIssue() {
        const { id, owner, repo, title, link, desc, labels, accessToken } = this
        return http.post(`/repos/${owner}/issues`, {
            access_token: accessToken,
            title,
            repo,
            labels: labels.concat(['giteement', id]),
            body: `${link}\n\n${desc}`,
        })
            .then((meta) => {
                this.state.meta = meta
                return meta
            })
    }

    getIssue() {
        if (this.state.meta.id) return Promise.resolve(this.state.meta)

        return this.loadMeta()
    }

    post(body) {
        const { accessToken } = this
        return this.getIssue()
            .then(issue => http.post(issue.url + '/comments', { access_token: accessToken,body }, ''))
            .then(data => {
                this.state.meta.comments++
                const pageCount = Math.ceil(this.state.meta.comments / this.perPage)
                if (this.state.currentPage === pageCount) {
                    this.state.comments.push(data)
                }
                return data
            })
    }

    loadMeta() {
        const { id, owner, repo } = this
        return http.get(`/repos/${owner}/${repo}/issues`, {
            creator: owner,
            labels: id,
        })
            .then(issues => {
                if (!issues.length) return Promise.reject(NOT_INITIALIZED_ERROR)
                this.state.meta = issues[0]
                return issues[0]
            })
    }

    loadComments(page = this.state.currentPage) {
        return this.getIssue()
            .then(issue => http.get(issue.url + '/comments', { page, per_page: this.perPage }, ''))
            .then((comments) => {
                this.state.comments = comments
                return comments
            })
    }

    loadUserInfo() {
        if (!this.accessToken || "undefined" == this.accessToken) {
            this.logout()
            return Promise.resolve({})
        }

        return http.get('/user', {
            token: this.accessToken
        })
            .then((user) => {
                this.state.user = user
                localStorage.setItem(LS_USER_KEY, JSON.stringify(user))
                return user
            })
    }

    login() {
        window.location.href = this.loginLink
    }

    logout() {
        localStorage.clear();
        this.state.user = {}
    }

    goto(page) {
        this.state.currentPage = page
        this.state.comments = undefined
        return this.loadComments(page)
    }
}
module.exports = Giteement
